﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input;

using Faust.Andrew.MTG.Helper;
using System.Reflection;

namespace Faust.Andrew.MTG.Player
{
    public class GamePresenter : DependencyObject
    {
        
        protected IMainView _mainView;
        private PlanesControl _planeView;
        protected ITableView _tableView;
        protected IPlayersView _playersView;
        protected ICardListView _handView;
        protected ICardListView _workspaceView;
        protected IPreviewView _previewView;
        protected INotificationView _notificationsView;
        protected IControlsView _controlsView;

        protected ImageSource _cardBackImage;


        #region DependencyProperties
        public static readonly DependencyProperty IsSoundEnabledProperty = DependencyProperty.Register("IsSoundEnabled", typeof(bool), typeof(GamePresenter));

        public bool IsSoundEnabled
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (bool)GetValue(IsSoundEnabledProperty);
            }
            set
            {
                SetValue(IsSoundEnabledProperty, value);
            }
        }
        
        #endregion


        #region Initialization
        public GameModel Model { get; set; }        

        public void SetView(IMainView view)
        {
            _mainView = view;
        }

        public void SetView(ITableView view)
        {
            _tableView = view;
        }

        public void SetView(IPlayersView view)
        {
            _playersView = view;
        }        

        public GamePresenter()
        {
            Model = new GameModel(this);            

            LoadImages();            
        }

        protected void LoadImages()
        {
            _cardBackImage = Properties.Resources.CardBack.ToImageSource();        
        }

        public void RefreshGameState()
        {
            _tableView.RefreshPlayers();
            _playersView.RefreshPlayers();
        }
        #endregion

        #region Sounds
        public void PlayDieRollSound()
        {
            PlaySound(Properties.Resources.Dice);
        }

        public void PlayNewTurnSound()
        {
            PlaySound(Properties.Resources.Ding);
        }

        public void PlayShuffleSound()
        {
            PlaySound(Properties.Resources.Shuffle);
        }

        public void PlaySound(System.IO.Stream stream)
        {
            if (!IsSoundEnabled) { return; }

            using (System.Media.SoundPlayer player = new System.Media.SoundPlayer(stream))
            {
                player.Play();
            }
        }
        #endregion

        #region DragDrop
        protected Point _dragStartPoint;
        protected Point _relativeStartPoint;
        protected bool _isDragging;
        protected bool _isRightDragging;
        protected bool _isDraggingDeck;
        protected FrameworkElement _sourceHost;
        protected FrameworkElement _dragElement;        
        protected Cursor _dragCursor;

        protected bool CanMove(FrameworkElement element)
        {
            if (element is Card)
            {
                Card card = element as Card;
                return Model.CanMoveCard(card.GameCard);
            }
            else if ((element.DataContext is Shared.Counter))
            {
                return true;
            }
            else if ((element.DataContext is Shared.GameCard))
            {
                Shared.GameCard card = (Shared.GameCard)element.DataContext;
                return Model.CanMoveCard(card);
            }
            else
            {
                return false;
            }
        }

        public void Deck_PreviewMouseLeftButtonDown(FrameworkElement element, MouseButtonEventArgs e, bool consumeEvent)
        {
            PreviewMouseButtonDown(element, e, consumeEvent, false, true);
        }

        public void Deck_PreviewMouseRightButtonDown(FrameworkElement element, MouseButtonEventArgs e, bool consumeEvent)
        {
            PreviewMouseButtonDown(element, e, consumeEvent, true, true);
        }

        //public void PreviewMouseLeftButtonDown(FrameworkElement element, MouseButtonEventArgs e)
        //{
        //    PreviewMouseLeftButtonDown(element, e, true);
        //}

        public void PreviewMouseLeftButtonDown(FrameworkElement element, MouseButtonEventArgs e, bool consumeEvent)
        {
            PreviewMouseButtonDown(element, e, consumeEvent, false, false);
        }

        public void PreviewMouseRightButtonDown(FrameworkElement element, MouseButtonEventArgs e, bool consumeEvent)
        {
            PreviewMouseButtonDown(element, e, consumeEvent, true, false);
        }

        protected void PreviewMouseButtonDown(FrameworkElement element, MouseButtonEventArgs e, bool consumeEvent, bool isRightMouseButton, bool isDeck)
        {
            if (!isDeck && !CanMove(element)) { return; }
            System.Diagnostics.Trace.WriteLine("PreviewMouseButtonDown");
            e.Handled = consumeEvent;
            _dragElement = null;
            _dragCursor = null;
            _isDraggingDeck = isDeck;
            _isRightDragging = isRightMouseButton;
            _dragStartPoint = e.GetPosition(null);
            _relativeStartPoint = e.GetPosition(element);
        }

        //public void PreviewMouseMoveFromDeck(FrameworkElement element, MouseEventArgs e, PlayerSurface sourceHost)
        //{
        //    //_isDraggingDeck = true;
        //    PreviewMouseMove(element, e, sourceHost);
        //}

        public void PreviewMouseMove(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            if (!_isDraggingDeck && !CanMove(element)) { return; }

            if ((e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed) && !_isDragging)
            {
                System.Diagnostics.Trace.WriteLine("PreviewMouseMove");
                Point position = e.GetPosition(null);

                if (Math.Abs(position.X - _dragStartPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(position.Y - _dragStartPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
                {
                    StartDrag(element, e, sourceHost);
                }
            }
        }

        protected void StartDrag(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            if (element is Card)
            {
                StartCardDrag(element, e, sourceHost);
            }
            else if (element.DataContext is Shared.Counter)
            {
                StartCounterDrag(element, e, sourceHost);
            }
            else if (element.DataContext is Shared.GameCard)
            {
                StartGameCardDrag(element, e, sourceHost);
            }
            else if (_isDraggingDeck)
            {
                StartDeckDrag(element, e, sourceHost);
            }

            e.Handled = true;
        }

        private void StartDeckDrag(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            System.Diagnostics.Trace.WriteLine("StartDeckDrag");
            DataObject data = GetDataObject("Deck");
            _dragCursor = CursorHelper.CreateCursor(element, _mainView.GetZoomLevel(), _tableView.GetRotation(element));
            StartDrag(data, element, e, sourceHost);
        }

        private void StartGameCardDrag(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            System.Diagnostics.Trace.WriteLine("StartGameCardDrag");
            Shared.GameCard gameCard = (Shared.GameCard)element.DataContext;
            DataObject data = GetDataObject(gameCard);
            _dragCursor = CursorHelper.CreateCursor(element, _mainView.GetZoomLevel(), _tableView.GetRotation(element));
            StartDrag(data, element, e, sourceHost);
            
            //_isDragging = true;

            //_sourceHost = sourceHost;
            //_dragElement = element;
            //_dragCursor = CursorHelper.CreateCursor(_dragElement, _mainView.GetZoomLevel(), _tableView.GetRotation(element));
            //sourceHost.GiveFeedback += Source_GiveFeedback;

            //
            //DragDrop.DoDragDrop(sourceHost, data, DragDropEffects.Move);

            //sourceHost.GiveFeedback -= Source_GiveFeedback;

            //_isDragging = false; 
        }

        protected void StartCounterDrag(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            Shared.Counter counter = element.DataContext as Shared.Counter;
            DataObject data = GetDataObject(counter);
            _dragCursor = CursorHelper.CreateCursor(element, 1.0, 0);
            StartDrag(data, element, e, sourceHost);

            //_isDragging = true;

            //_sourceHost = sourceHost;
            //_dragElement = element;
            //_dragCursor = CursorHelper.CreateCursor(_dragElement, 1.0, 0);
            //sourceHost.GiveFeedback += Source_GiveFeedback;

            
            //DragDrop.DoDragDrop(sourceHost, data, DragDropEffects.Copy);

            //sourceHost.GiveFeedback -= Source_GiveFeedback;

            //_isDragging = false;

        }

        protected void StartDrag(DataObject data, FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            _isDragging = true;

            _sourceHost = sourceHost;
            _dragElement = element;
            
            sourceHost.GiveFeedback += Source_GiveFeedback;

            DragDrop.AddQueryContinueDragHandler(sourceHost, QueryContinueDragEvent);
            DragDrop.DoDragDrop(sourceHost, data, DragDropEffects.Move);

            sourceHost.GiveFeedback -= Source_GiveFeedback;

            _isDragging = false;
        }

        private void QueryContinueDragEvent(object sender, QueryContinueDragEventArgs e)
        {
            e.Handled = true;

            if (e.EscapePressed)
            {
                e.Action = DragAction.Cancel;
                return;
            }

            e.Action = DragAction.Drop;

            if ((e.KeyStates & DragDropKeyStates.LeftMouseButton) != DragDropKeyStates.None)
            {
                // Still dragging with Left Mouse Button
                e.Action = DragAction.Continue;
            }
            else if ((e.KeyStates & DragDropKeyStates.RightMouseButton) != DragDropKeyStates.None)
            {
                // Still dragging with Right Mouse Button
                e.Action = DragAction.Continue;
            }
        }

        protected void StartCardDrag(FrameworkElement element, MouseEventArgs e, FrameworkElement sourceHost)
        {
            Shared.GameCard gameCard = (element as Card).GameCard;
            DataObject data = GetDataObject(element);

            _dragCursor = CursorHelper.CreateCursor(element, _mainView.GetZoomLevel(), _tableView.GetRotation(element));
            StartDrag(data, element, e, sourceHost);

            //_isDragging = true;

            //_sourceHost = sourceHost;
            //_dragElement = element;

            //_dragCursor = CursorHelper.CreateCursor(_dragElement, _mainView.GetZoomLevel(), _tableView.GetRotation(element));
            //sourceHost.GiveFeedback += Source_GiveFeedback;


            //DragDrop.DoDragDrop(sourceHost, data, DragDropEffects.Move);

            //sourceHost.GiveFeedback -= Source_GiveFeedback;

            //_isDragging = false;
        }

        void Source_GiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            if (_dragCursor != null)
            {
                Mouse.SetCursor(_dragCursor);
                e.UseDefaultCursors = false;
                e.Handled = true;                
            }            
        }        

        protected DataObject GetDataObject(object element)
        {
            if (element is Card)
            {
                return new DataObject(typeof(Shared.GameCard), (element as Card).GameCard);
            }
            else
            {
                return new DataObject(element.GetType(), element);
            }
        }

        public void DropOnPlayer(PlayerState player, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Shared.Counter)))
            {
                e.Handled = true;
                Shared.Counter counter = (Shared.Counter)e.Data.GetData(typeof(Shared.Counter));
                Model.AddCounter(player, counter);
            }
        }

        public void DropOnCard(Shared.GameCard gameCard, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Shared.Counter)))
            {
                //A counter was dropped.
                e.Handled = true;

                Shared.Counter counter = (Shared.Counter)e.Data.GetData(typeof(Shared.Counter));
                Model.AddCounter(gameCard, counter);
            }
        }

        public void DropOnGraveyard(DragEventArgs e)
        {
            Shared.GameCard card = GetDroppedCard(e);
            if (card != null)
            {
                e.Handled = true;
                Model.SendToGraveyard(card);
            }
        }

        public void DropOnDeck(DragEventArgs e)
        {
            Shared.GameCard card = GetDroppedCard(e);
            if (card != null && !(card.IsToken))
            {
                e.Handled = true;
                Model.SendToDeck(card, Shared.DeckPosition.Top, 0);
            }
        }

        public void DropOnCardList(DragEventArgs e)
        {
            Shared.GameCard card = GetDroppedCard(e);
            if (card != null && !(card.IsToken))
            {
                System.Diagnostics.Trace.WriteLine("DropOnCardList");
                if (e.Source == _handView)
                {
                    e.Handled = true;
                    Model.SendToHand(card);
                }
                else if (e.Source == _workspaceView)
                {
                    e.Handled = true;
                    Model.SendToWorkspace(card);
                }                
            }
        }
        
        public void DropOnPlayspace(DragEventArgs e)
        {
            Shared.GameCard card = GetDroppedCard(e);
            if (card != null)
            {
                DropCardOnPlayspace(card, e);
            }

            _isDragging = false;
        }

        protected Shared.GameCard GetDroppedCard(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Shared.GameCard)))
            {
                return (Shared.GameCard)e.Data.GetData(typeof(Shared.GameCard));
            }
            else if (e.Data.GetDataPresent(typeof(string)))
            {
                string data = (string)e.Data.GetData(typeof(string));
                if ("Deck" == data)
                {
                    //It's being dragged from the deck. The card needs to be drawn from the top
                    //of the deck first.
                    return Model.DrawCard();
                }
            }

            return null;
        }

        protected void DropCardOnPlayspace(Shared.GameCard card, DragEventArgs e)
        {
            if (!(e.Source is PlayerSurface)) { return; }
            PlayerSurface targetSurface = e.Source as PlayerSurface;

            Point position = e.GetPosition(targetSurface);

            card.Location.X = position.X;
            card.Location.Y = position.Y;

            if (_isRightDragging)
            {
                card.IsFaceDown = true;
            }

            if (targetSurface.Player.PlayerID == card.Location.PlayerID)
            {
                card.Location.Zone = Faust.Andrew.MTG.Player.Shared.Zone.InPlay;
                Model.MoveCard(card);
            }
            else
            {
                Model.MoveCard(Model.PlayerByID(card.Location.PlayerID), targetSurface.Player, card);
            }
        }
        #endregion

        #region IPlayersView
        public object CreatePlayersView()
        {
            _playersView = new PlayersControl();
            _playersView.Presenter = this;
            return _playersView;
        }
        #endregion

        #region IMainView
        public void ReportError(string message)
        {
            _mainView.ReportError(message);
        }
        public bool GetHostParameters(List<string> decks, ref string name, ref int port, ref string deckName, ref bool usePlanes)
        {
            return _mainView.GetHostParameters(decks, ref name, ref port, ref deckName, ref usePlanes);    
        }

        public bool GetJoinParameters(List<string> decks, ref string name, ref string host, ref int port, ref string deckName, ref bool isRejoin)
        {
            return _mainView.GetJoinParameters(decks, ref name, ref host, ref port, ref deckName, ref isRejoin);    
        }        

        public void RaiseCommunicationsException(CommunicationExceptionEventArgs e)
        {
            _mainView.RaiseCommunicationsException(e);
        }

        public int GetTargetPlayer()
        {
            return _mainView.GetTargetPlayer();
        }

        public List<int> GetTargetPlayers()
        {
            return _mainView.GetTargetPlayers();
        }

        public RandomCardParameters GetRandomRevealParameters()
        {
            return _mainView.GetRandomRevealParameters();
        }
        
        public RandomCardParameters GetRandomDiscardParameters()
        {
            return _mainView.GetRandomDiscardParameters();
        }

        #endregion

        #region ITableView
        public System.Windows.Point MeasurePlayArea(int playerID)
        {
            return _tableView.MeasurePlayArea(playerID);
        }

        public System.Windows.Point MeasureCard(int cardID)
        {
            return _tableView.MeasureCard(cardID);
        }        
        #endregion

        #region HandView
        //private HandWindow _handWindow;
        //public void ViewHand()
        //{
        //    if (_handWindow == null || _handWindow.IsLoaded == false)
        //    {
        //        _handWindow = new HandWindow();
        //        _handWindow.Title = "Hand";
        //        _handView = _handWindow;
        //        _handView.Presenter = this;
        //        _handView.CardList = this.Model.Hand;
        //    }

        //    _handWindow.Show();
        //}        

        public ICardListView CreateHandView()
        {
            _handView = new CardListControl();
            _handView.Presenter = this;
            _handView.CardList = Model.Hand;
            return _handView;
        }

        public double GetHandZoom()
        {
            return _handView.Zoom;
        }

        public void SetHandZoom(double zoom)
        {
            _handView.Zoom = zoom;
        }
        #endregion

        #region WorkspaceView
        //private HandWindow _workspaceWindow;
        //public void ViewWorkspace()
        //{
        //    if (_workspaceWindow == null || _workspaceWindow.IsLoaded == false)
        //    {
        //        _workspaceWindow = new HandWindow();
        //        _workspaceWindow.Title = "Workspace";
        //        _workspaceView = _workspaceWindow;
        //        _workspaceWindow.Presenter = this;
        //        _workspaceWindow.CardList = Model.Workspace;
        //    }

        //    _workspaceWindow.Show();
        //}
        
        //public void SetWorkspaceView(ICardListView workspaceView)
        //{
        //    _workspaceView = workspaceView;
        //    _workspaceView.Presenter = this;
        //    _workspaceView.CardList = Model.Workspace;
        //}

        public ICardListView CreateWorkspaceView()
        {
            _workspaceView = new CardListControl();
            _workspaceView.Presenter = this;
            _workspaceView.CardList = Model.Workspace;
            return _workspaceView;
        }

        public double GetWorkspaceZoom()
        {
            return _workspaceView.Zoom;
        }

        public void SetWorkspaceZoom(double zoom)
        {
            _workspaceView.Zoom = zoom;
        }
        #endregion

        #region PlanesView
        public object CreatePlanesView()
        {
            _planeView = new PlanesControl();
            _planeView.Presenter = this;
            return _planeView;

        }
        #endregion

        #region NotificationsView
        public object CreateNotificationsView()
        {
            NotificationControl notification = new NotificationControl();
            notification.EnableAutoScroll = true;
            _notificationsView = notification;
            
            _notificationsView.Presenter = this;
            return _notificationsView;
        }
        #endregion

        #region PreviewView

        public IPreviewView CreatePreviewView()
        {
            _previewView = new CardViewControl();
            _previewView.Presenter = this;            
            return _previewView;
        }

        public void PreviewCard(FrameworkElement element)
        {
            _previewView.Preview(element);
        }

        public void PreviewCard(Shared.GameCard card)
        {
            _previewView.Preview(card);
        }

        public object GetCardTooltip(Shared.GameCard card)
        {
            if (card.IsFaceDown && (Model.CurrentPlayer.PlayerID != card.ControllerID))
            {
                return null;
            }
            else
            {
                return card.CardText;
            }
        }

        public ImageSource GetCardPreview(Shared.GameCard card)
        {
            if (card.IsFaceDown && (Model.CurrentPlayer.PlayerID != card.ControllerID))
            {
                return _cardBackImage;
            }
            else
            {
                return card.ImageSource;
            }
        }

        #endregion

        #region ControlsView
        public object CreateControlsView()
        {
            _controlsView = new ControlBox();
            _controlsView.Presenter = this;
            return _controlsView;
        }
        #endregion

        public void ViewGraveyard(int playerId)
        {
            HandWindow graveYard = new HandWindow();
            graveYard.Title = "Graveyard";
            graveYard.Presenter = this;
            graveYard.CardList = Model.GetGraveyard(playerId);
            graveYard.RemoveManually = true;

            graveYard.Show();
        }

        public void ViewDeck(int playerId)
        {
            HandWindow deck = new HandWindow();
            deck.Title = "Deck";
            deck.Presenter = this;
            deck.CardList = Model.GetDeck(playerId);
            deck.RemoveManually = true;

            deck.Show();
        }

        public void ViewCards(ObservableCollection<Shared.GameCard> cards)
        {
            HandWindow deck = new HandWindow();
            deck.Title = "Cards";
            deck.Presenter = this;
            deck.CardList = cards;

            deck.ShowDialog();
        }
        
        public void AddToken()
        {
            AddTokenWindow tokenWindow = new AddTokenWindow();
            tokenWindow.Presenter = this;
            if (tokenWindow.ShowDialog() == true)
            {
                Token token = tokenWindow.Token;

                string quantityString = InputBox.ShowInput("How many tokens to put in play?", "1");
                int quantity;
                if (!Int32.TryParse(quantityString, out quantity) || quantity < 1)
                {
                    MessageBox.Show("You must provide a quantity > 0");
                    return;
                }

                if (quantity > 10)
                {
                    MessageBox.Show("Creating more than 5 tokens at a time is not allowed.");
                    return;
                }

                Model.PlayTokenCreature(token, quantity);
            }
        }

        #region GameStartingNotifications
        public void StartingGameLoading()
        {
            _mainView.StartingGameLoading();
        }

        public void ReportGameLoadProgress(int cardNumber, int totalCards)
        {
            _mainView.ReportGameLoading(cardNumber, totalCards);
        }

        public void FinishedGameLoading()
        {
            _mainView.FinishedGameLoading();
        }
        #endregion
    }
}
